#!/usr/bin/env python
#
#       Incoming.py
#       
#       Copyright 2009 Ben Davis <its.hip@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import pickle
import random
import zlib

import Orders
import Parser
import ToClient

from Clients import Clients
from Maps import Map
from Players import Players

clients = Clients()
map = Map()
players = Players()

class Packet:
    def __call__(self, client):
        pass
        
class EndTurn(Packet):
    def __call__(self, client):
        # If it is currently that player's turn
        if client.player.key() == players.turn: # only the current player can set it off.
            #nextplz
            players.next()
            for fclient in clients:
                if fclient.player.key() == players.turn: #this player is the new one
                    if fclient.player.type != "player":
                        #skip a non-player. Need to proof this against multiple non-players.
                        print fclient.player.key(), "is not a player."
                        players.next()
                        break;
                    if fclient.player.ships == {}:
                        print fclient.player.key(), "has no ships."
                        players.next()
                        break;
            clients.send(ToClient.NewTurn(players.turn))
            for ship in players[players.turn].ships.values():
                ship.poll()
                
class Fire(Packet):
    def __call__(self, client):
        # If not this player's turn
        if client.player.key() != players.turn:
            client.send(ToClient.Message("Cannot fire whilst it is not your turn"))
            return
        try:
            ship = client.player.ships[self.ship]
        except KeyError:
            # Ship does not belong to player
            client.send(ToClient.Message("This ship does not belong to you"))
            return
        weapon = ship.weapons[self.weapon]
        # If ship has enough steam to fire
        if ship.steam >= weapon.steam:
            # Create a new list for the ships damaged
            damaged = []
            broken = False
            # For each direction the weapon fires
            for direction in weapon.targets:
                for x, y in direction:
                    # Get the tile that the weapon fires at
                    tile = ship.get_tile_direction_relative(x, y, 0)
                    # Loop through all ships
                    for player in players.values():
                        for target in player.ships.values():
                            # If this ship is hit (targetted)
                            if (target.x, target.y) == tile:
                                # Act on the ship
                                hit = random.randint(1, 12)
                                # If weapon hits
                                if hit <= weapon.to_hit:
                                    # If we rolled a 1
                                    if hit == 1:
                                        # Cause critical damage
                                        damage = weapon.crt_damage
                                    else:
                                        # Otherwise, just cause normal damage
                                        damage = random.randint(weapon.min_damage, weapon.max_damage)
                                    # Append a tuple of (ship, player, damage) to the damaged list
                                    damaged.append((target.key(), target.player, damage))
                                    # Damage the ship
                                    players[target.player].ships[target.key()].health -= damage
                                    #print target.key(), "takes", damage, "damage",
                                    # If ship has no health left
                                    if target.health <= 0:
                                        # Destroy it
                                        map.logic.shipded(ToClient, players, target.player, target.key())
                                        del(players[target.player].ships[target.key()])
                                        #print "and is destroyed"
                                    else:
                                        map.logic.shipdamaged(ToClient, players, client.player.key(), ship.key(), target.player, target.key(), damage)
                                # If we want to stop at the first target
                                #note: this goes from the topleft/right, not closest.
                                if weapon.gun:
                                    broken = True
                                    # Then stop
                                    break
                        if broken:
                            break
 #                   if broken:
#                        break
            clients.send(ToClient.Fire(ship, self.weapon, damaged))
            ship.steam -= weapon.steam
            

class Move(Packet):
    def __call__(self, client):
        # If not this player's turn
        if client.player.key() != players.turn:
            client.send(ToClient.Message("Cannot move ships whilst it is not your turn"))
            return
        orders = Orders.Orders()
        try:
            ship = client.player.ships[self.ship]
        except KeyError:
            # Ship does not belong to player
            client.send(ToClient.Message("This ship does not belong to you"))
            return
        
        try:
            steam = 0
            x, y, r = ship.pos()
            #print ship.key(), "wants to move from", (x, y), "to", self.path[-1]
            path_x, path_y, path_r = ship.pos()
            for x, y in self.path:
                r = ship.get_direction_to_tile_relative(x - path_x, y - path_y)
                angle = r - path_r
                if angle > 0:
                    if angle > 4:
                        # Turn port
                        angle = path_r - r
                        while angle > 0:
                            angle += -1
                            steam += 1
                            orders.append(Orders.Rotate(-1))
                    else:
                        # Turn starboard
                        while angle > 0:
                            angle += -1
                            steam += 1
                            orders.append(Orders.Rotate(1))
                steam += 1
                orders.append(Orders.Move())
                #entities.selected.x, entities.selected.y, entities.selected.r = x, y, r
                path_x, path_y, path_r = x, y, r
            if ship.steam >= steam:
                orders.steam = steam
                ship.x, ship.y, ship.r = x, y, r
                ship.steam += -steam
                clients.send(ToClient.Orders(ship, orders))
        except TypeError:
            return
        
class Rotate(Packet):
    def __call__(self, client):
        # If not this player's turn
        if client.player.key() != players.turn:
            client.send(ToClient.Message("Cannot rotate ships whilst it is not your turn"))
            return
        try:
            ship = client.player.ships[self.ship]
        except KeyError:
            client.send(ToClient.Message("This ship does not belong to you"))
            return
        orders = Orders.Orders()
        steam = 0
        try:
            angle = self.r - ship.r
            if angle > 0:
                if angle > 4:
                    # Turn port
                    angle = ship.r - self.r
                    while angle > 0:
                        angle += -1
                        steam += 1
                        orders.append(Orders.Rotate(-1))
                else:
                    # Turn starboard
                    while angle > 0:
                        angle += -1
                        steam += 1
                        orders.append(Orders.Rotate(1))
            if ship.steam >= steam:
                ship.r = self.r
                ship.steam += -steam
                orders.steam = steam
                clients.send(ToClient.Orders(ship, orders))
                
        except (AttributeError, TypeError):
            pass
            
class Say(Packet):
    def __call__(self, client):
        # If the player-text begins with a "&"
        if self.text[0] == "&":
            # Debug code -- send to parser sans the "&"
            client.parser.do(self.text[1:])
        # If it doesn't begin with a "&"
        else:
            # Send the text to all clients
            clients.send(ToClient.Said(client.player, self.text))

class ChoosePlayer(Packet):
    def __call__(self, client):
        print client, "chose to be player", str(self.choice)
        client.chosen = self.choice

            


